Understanding Object Initializer Syntax

As seen throughout this chapter, a constructor allows you specify start up values when creating a new object. On a related note, properties allow you to get and set underlying data in a safe manner. When you are working with other people's classes, including the classes found within the .NET base class library, it is not too uncommon to discover that there is not a single constructor which allows you to set every piece of underlying state data. Given this point, a programmer is typically forced to pick the best constructor possible, after which he or she makes assignments using a handful of provided properties.

To help streamline the process of getting an object up and running, C# offers object initializer syntax. Using this technique, it is possible to create a new object variable and assign a slew of properties and/or public fields in a few lines of code. Syntactically, an object initializer consists of a comma delimited list of specified values, enclosed by the { and } tokens. Each member in the initialization list maps to the name of a public field or public property of the object being initialized.

To see this syntax in action, create a new Console Application named ObjectInitializers. Now, consider a simple class named Point, created using automatic properties (which is not mandatory for this example, but helps us write some very concise code):

class Point
{
    public int X { get; set; }
    public int Y { get; set; }

    public Point(int xVal, int yVal)
    {
        X = xVal;
        Y = yVal;
    }

    public Point() { }

    public void DisplayStats()
    {
        Console.WriteLine("[{0}, {1}]", X, Y);
    }
}

Now consider how we can make Point objects using any of the following approaches:

static void Main(string[] args)
{
    Console.WriteLine("***** Fun with Object Init Syntax *****\n");

    // Make a Point by setting each property manually.
    Point firstPoint = new Point();
    firstPoint.X = 10;
    firstPoint.Y = 10;
    firstPoint.DisplayStats();

    // Or make a Point via a custom constructor.
    Point anotherPoint = new Point(20, 20);
    anotherPoint.DisplayStats();

    // Or make a Point using object init syntax.
    Point finalPoint = new Point { X = 30, Y = 30 };
    finalPoint.DisplayStats();
    Console.ReadLine();
}

The final Point variable is not making use of a custom constructor (as one might do traditionally), but is rather setting values to the public X and Y properties. Behind the scenes, the type's default constructor is invoked, followed by setting the values to the specified properties. To this end, object initialization syntax is just shorthand notations for the syntax used to create a class variable using a default constructor, and setting the state data property by property.

Calling Custom Constructors with Initialization Syntax

The previous examples initialized Point types by implicitly calling the default constructor on the type:

// Here, the default constructor is called implicitly.
Point finalPoint = new Point { X = 30, Y = 30 };

If you wish to be very clear about this, it is permissible to explicitly call the default constructor as follows:

// Here, the default constructor is called explicitly.
Point finalPoint = new Point() { X = 30, Y = 30 };

Do be aware that when you are constructing a type using the new initialization syntax, you are able to invoke any constructor defined by the class. Our Point type currently defines a two-argument constructor to set the (x, y) position. Therefore, the following Point declaration results in an X value of 100 and a Y value of 100, regardless of the fact that our constructor arguments specified the values 10 and 16:

// Calling a custom constructor.
Point pt = new Point(10, 16) { X = 100, Y = 100 };

Given the current definition of your Point type, calling the custom constructor while using initialization syntax is not terribly useful (and more than a bit verbose). However, if your Point type provides a new constructor that allows the caller to establish a color (via a custom enum named PointColor), the combination of custom constructors and object initialization syntax becomes clear. Assume you have updated Point as follows:

public enum PointColor
{ LightBlue, BloodRed, Gold }

class Point
{
    public int X { get; set; }
    public int Y { get; set; }
    public PointColor Color{ get; set; }

    public Point(int xVal, int yVal)
    {
        X = xVal;
        Y = yVal;
        Color = PointColor.Gold;
    }

    public Point(PointColor ptColor)
    {
        Color = ptColor;
    }
    
    public Point()
        : this(PointColor.BloodRed){ }
    public void DisplayStats()
    {
        Console.WriteLine("[{0}, {1}]", X, Y);
        Console.WriteLine("Point is {0}", Color);
    }
}
 

With this new constructor, you can now create a golden point (positioned at 90, 20) as follows:

// Calling a more interesting custom constructor with init syntax.
Point goldPoint = new Point(PointColor.Gold){ X = 90, Y = 20 };
Console.WriteLine("Value of Point is: {0}", goldPoint.DisplayStats());

Initializing Inner Types

As briefly mentioned earlier in this chapter (and fully examined in Chapter 6), the "has-a" relationship allows us to compose new classes by defining member variables of existing classes. For example, assume you now have a Rectangle class, which makes use of the Point type to represent its upper-left/bottomright coordinates. Since automatic properties set all internal class variables to null, you will implement this new class using 'traditional' property syntax:

class Rectangle
{
    private Point topLeft = new Point();
    private Point bottomRight = new Point();

    public Point TopLeft
    {
        get { return topLeft; }
        set { topLeft = value; }
    }

    public Point BottomRight
    {
        get { return bottomRight; }
        set { bottomRight = value; }
    }

    public void DisplayStats()
    {
        Console.WriteLine("[TopLeft: {0}, {1}, {2} BottomRight: {3}, {4}, {5}]",
        topLeft.X, topLeft.Y, topLeft.Color,
        bottomRight.X, bottomRight.Y, bottomRight.Color);
    }
}

Using object initialization syntax, you could create a new Rectangle variable and set the inner Points as follows:

// Create and initialize a Rectangle.
Rectangle myRect = new Rectangle
{
    TopLeft = new Point { X = 10, Y = 10 },
    BottomRight = new Point { X = 200, Y = 200}
};

Again, the benefit of object initialization syntax is that it basically decreases the number of keystrokes (assuming there is not a suitable constructor). Here is the traditional approach to establishing a similar Rectangle:

// Old-school approach.
Rectangle r = new Rectangle();
Point p1 = new Point();
p1.X = 10;
p1.Y = 10;
r.TopLeft = p1;
Point p2 = new Point();
p2.X = 200;
p2.Y = 200;
r.BottomRight = p2;

While you might feel object initialization syntax can take a bit of getting used to, once you get comfortable with the code, you'll be quite pleased at how quickly you can establish the state of a new objectwith minimal fuss and bother.

To wrap up this chapter, allow me to close with three bite-sized topics which will round out your understanding of building well encapsulated classes, specifically constant data, read only fields, and partial class definitions.

Source Note The ObjectInitilizers project can be found under the Chapter 5 subdirectory.